package org.example.Pojo;

import org.example.Util.FileUtil;

import javax.swing.*;
import java.util.*;
import java.util.concurrent.CountDownLatch;

public class Gamepojo implements Runnable{
    private static JLabel[][] sectionImg;//切片矩阵，切片的位置不会发生改变，只有映射矩阵会发生改变
    private static int[][] map;//数字映射矩阵
    private JLabel completeImg;//参考图片
    private int [] disrupt;//打乱数组
    private int [] end;//结果数组
    private FileUtil fileUtil;
    private boolean move;//切片是否可移动
    private int num;//逆序列数的个数
    private Blank blank;//可移动位对象
    public  boolean begin;//按钮选择器,1为重新开始
    public  boolean back;//刷新界面判断,1为刷新界面
    private int theory;//理论步数
    private int[] norm;//计算逆序列的数组

    public Gamepojo(int length,String fileingex) {
        //文件读取工具类的初始化
        fileUtil=new FileUtil();
        //sectionImg初始化
        sectionImg=new JLabel[length][length];
        sectionImg=fileUtil.ReadsectionImg(fileingex,length);
        //map初始化
        map=new int[length][length];
        //disrupt和nodisrupt初始化
        disrupt=new int[length*length];
        end=new int[length*length];
        for(int i=0;i<length;i++){
            for(int j=0;j<length;j++){
                map[i][j]=i*length+j;
                disrupt[i*length+j]=i*length+j;
            }
        }
        for(int i=0;i<length*length-1;i++)
            end[i]=i+1;
        end[length*length-1]=0;
        //可移动位的初始化
        blank=new Blank(length-1,length-1);
        //完整图片的初始化
        completeImg=fileUtil.ReadCompeleteImg(fileingex);
        //是否可以正常游戏
        move=false;
        //测试成绩数据
        //move=true;
        //选择器初始化
        begin=false;
        back=false;
        //逆序列计算数组初始化
        norm=new int[length*length];
    }
    public boolean getmove(){
        return move;
    }
    public int[] getDisrupt() {
        return disrupt;
    }
    public int[][] getMap() {
        return map;
    }
    public JLabel getCompleteImg() {
        return completeImg;
    }
    public JLabel[][] getSectionImg() {
        return sectionImg;
    }

    public int getTheory() {
        return theory;
    }

    //空白位
    public Blank getBlank() {
        return blank;
    }
    //重新开始就是把可以正常游戏改为不可以正常游戏
    public void setMove(boolean move) {
        this.move = move;
    }

    //随机一维数组
    public void shuffle(int[] arr) {
        Random random=new Random();//不对一维数组的最后一位随机
        for(int i=arr.length-2;i>=0;i--){
            int index=random.nextInt(i+1);
            int tem=arr[index];
            arr[index]=arr[i];
            arr[i]=tem;
        }
        normalization();
        //测试代码
//        for(int i=0;i<arr.length;i++){
//            System.out.print(arr[i]+" ");
//        }
    }

    //获取移动前坐标和移动后坐标
    public static void gain(int[] arr,int length) throws InterruptedException {
        CountDownLatch latch=new CountDownLatch(arr.length);
        for(int i=0;i< arr.length;i++){
            //移动前二维数组索引位
            int row1=arr[i]/length;
            int col1=arr[i]%length;
            //移动后二维数组索引位
            int row2=i/length;
            int col2=i%length;
            //开启切片动态移动线程
            MySection mySection=new MySection(row1,col1,col2,row2,sectionImg,latch);
            new Thread(mySection).start();
            //一维映射二维
            map[row2][col2]=arr[i];
            System.out.print(map[row2][col2]+" ");
//            if(i!=arr.length-1)
//                System.out.print(sectionImg[row2][col2].getIcon().toString()+" ");//检测代码
        }
        System.out.println(" ");
        latch.await();
        //System.out.println("线程全部结束");//检测代码

        //交换有问题
        JLabel[][]sectiontem=new JLabel[map.length][map.length];//暂存数组
        for(int i=0;i<map.length;i++){
            for(int j=0;j<map.length;j++){
                int row=map[i][j]/map.length;
                int col=map[i][j]%map.length;
                //图片数组交换
                sectiontem[i][j]= sectionImg[row][col];
//                    if(i!=map.length-1||j!=map.length-1)
//                System.out.print(sectiontem[i][j].getIcon().toString()+" ");
            }
        }
        sectionImg=sectiontem;//换回来
        //text();
    }

    //移动切片至可以正常游戏
    @Override
    public void run() {
        //这里有个bug等一下回来修-->当随机刚好没随机的时候也要打乱
        //先看看正常的状态
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        int length= map.length;
        while(!move){
            if(begin){
                try {
                    back();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }//重新开始的还原
            //System.out.println("a");//检测代码
            if(!back)
                shuffle(disrupt);
            try {
                gain(disrupt,length);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //等gain函数全部跑完才继续往下进行线程
            //判断
            num=0;
            move=judge();
            //当判断出不能正常游戏的时候停止三秒再进行界面移动
            if(!move){
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                if(!begin){
                    try {
                        back();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }//初始化还原
            }
        }
        //这里要对理论步数进行计算
        theory=countTheory();
        System.out.print("最少需要步数为"+theory);
        if(begin||back){
            blank.setRow(length-1);
            blank.setCol(length-1);
        }
        begin=true;
        back=false;
        //System.out.println("可以开始游戏了");
        //如果刷新界面或者重新开始需要对空白位进行更新
    }

    //无论什么情况都回退回最初始状态--》没有打乱前
    public void back() throws InterruptedException {
        CountDownLatch latch=new CountDownLatch(map.length* map.length);
        for(int i=0;i<map.length;i++){
            for(int j=0;j<map.length;j++){
                //移动前的索引位i,j
                //移动后的索引位
                int row2=map[i][j]/map.length;
                int col2=map[i][j]%map.length;
                //开启切片动态移动线程
                MySection mySection=new MySection(i,j,col2,row2,sectionImg,latch);
                new Thread(mySection).start();
            }
        }
        latch.await();
        JLabel[][]sectiontem=new JLabel[map.length][map.length];//暂存数组
        for(int i=0;i<map.length;i++){
            for(int j=0;j<map.length;j++){
                int row=map[i][j]/map.length;
                int col=map[i][j]%map.length;
                //图片数组交换
                sectiontem[row][col]= sectionImg[i][j];
                //还原map
                map[i][j]=i*map.length+j;
            }
        }
        sectionImg=sectiontem;//换回来
        //text();
    }

    //判断是否可以进行游戏
    private boolean judge() {
        Vector<Integer> tem=new Vector<Integer>();
        dfs(0,tem);
        System.out.println("逆序列数个数为"+num);
        int length=map.length;
        if(length%2!=num%2)return true;
        else return false;
    }
    //打乱数组标准化
    public void normalization(){
        for(int i=0;i<disrupt.length;i++){
            if(disrupt[i]!=disrupt.length-1)
                norm[i]=disrupt[i]+1;
            else
                norm[i]=0;
        }
    }
    //回溯算法-->算出逆序列个数
    public void dfs(int start,Vector<Integer>tem){
        if(tem.size()==2){
            //System.out.println(tem.get(0)+" "+tem.get(1));//测试代码
            if(tem.get(0)>tem.get(1)){
                num++;//逆序列数++
            }
            return;
        }
        for(int i=start;i<norm.length-1;i++){
            tem.add(norm[i]);
            dfs(i+1,tem);
            tem.remove(tem.size()-1);
        }
    }
    //八数码问题解决最短步数问题，这里有bug记得来修
    private String endSting;//结果数组转换为字符串
    private String beginString;//初始数组转换为字符串
    private Queue<String> q=new LinkedList<>();//队列存放字符串
    private Map<String,Integer> hashmap=new HashMap<>();//用来记录
    private final int [][]d={{1,0},{-1,0},{0,-1},{0,1}};//初始化移动数组
    public int countTheory(){
        endSting=ArraysToString(end);
        beginString=ArraysToString(norm);
//        endSting="123456780";
//        beginString="123456078";//测试代码
        System.out.println(endSting);
       System.out.println(beginString);
        q.add(beginString);//添加初始状态
        while(!q.isEmpty()){
            String tem=q.peek();//获取队列头元素
            q.remove();
            int dist=0;
            if(hashmap.get(tem)!=null)
                dist=hashmap.get(tem);
            if(Objects.equals(tem, endSting)) {
                hashmap.clear();
                q.clear();
                return dist;
            }
            for(int i=0;i<4;i++){
                int k=getk(tem);//空白位-->这里直接干出去了
                //将要和空白位移动的索引
                int x=k/map.length+d[i][0];
                int y=k%map.length+d[i][1];
                if(x>=0&&x<map.length&&y>=0&&y<map.length){
                    tem=swap(tem,k,x*map.length+y);
                    if(hashmap.get(tem)==null){
                        hashmap.put(tem,dist+1);
                        q.add(tem);
                        //System.out.println("本次移动结果为"+tem);
                    }//说明没有找到
                    tem=swap(tem,k,x*map.length+y);
                }
            }
        }
        hashmap.clear();
        q.clear();
        return -1;
    }
    //int类型数组转换为String
    public String ArraysToString(int []arr){
        StringBuilder res = new StringBuilder();
        if (arr == null)
            return "null";
        for(int tem : arr){
            if(tem==0)break;
            res.append(tem).append(" ");//链式编程
        }
        res.append("X").append(" ");
        return res.toString();
    }
    //计算索引位
    public int getk(String tem){
        String []str=tem.split(" ");
        for(int i=0;i<str.length;i++){
            if(str[i].equals("X"))
                return i;
        }
        return -1;
    }

    //交换算法
    public String swap(String tem, int index1, int index2){
        String[] strings=tem.split(" ");//按照空格为分割符
        String Tem=strings[index1];
        strings[index1]=strings[index2];
        strings[index2]=Tem;
        StringBuilder stringBuilder=new StringBuilder();
        for(String t:strings){
            stringBuilder.append(t).append(" ");
        }
        return stringBuilder.toString();
    }

    //测试图片数组
    private static void text(){
        for(int i=0;i<sectionImg.length;i++){
            for(int j=0;j<sectionImg.length;j++){
                if(i!=sectionImg.length-1||j!=sectionImg.length-1)
                    System.out.print(sectionImg[i][j].getIcon().toString()+" ");
            }
        }
        System.out.println(" ");
    }

}
